#include "game.hpp"
#include <iostream>
#include <queue>

using namespace Shiny;
using namespace Utils;

//
// Level
//
Level::~Level()
{
  while (_cameras.begin() != _cameras.end())
  {
    delete *(_cameras.begin());
    _cameras.erase(_cameras.begin());
  }
}

void Level::Render(sf::RenderTarget& target)
{
  std::list<Widgets::Container*>::iterator it  = _cameras.begin();
  std::list<Widgets::Container*>::iterator end = _cameras.end();

  for (; it != end ; ++it)
    (*it)->Render(target);
  if (_hud)
    _hud->Render(target);
  if (_displayFps) DoDisplayFps(target);
}

void Level::DoDisplayFps(sf::RenderTarget& target)
{
  if (_fpsClock.GetElapsedTime().AsSeconds() >= 1.f)
  {
    std::stringstream stream;

    stream << _fps;
    _fpsText.SetString(stream.str());
    _fpsClock.Restart();
    _fps = 0;
  }
  ++_fps;
  target.Draw(_fpsText);
}

void Level::AddView(Widgets::Container* view)
{
  _cameras.push_back(view);
}

void Level::DelView(Widgets::Container* view)
{
  std::list<Widgets::Container*>::iterator it  = _cameras.begin();
  std::list<Widgets::Container*>::iterator end = _cameras.end();
  
  while (it != end)
  {
    if (*it == view)
      it = _cameras.erase(it);
    else
      ++it;
  }
}

//
// Main Script
//
void MainScript::DrawMenuBackground(sf::RenderTarget& target)
{
  target.Clear(sf::Color::White);
}

//
// Game
//
Game::Game(Data data) : _errorManager(*this)
{
  RessourceData<sf::Texture>::Get()->SetDirectory("textures");
  RessourceData<sf::Sound>::Get()->SetDirectory("sounds");

  Data levels = data["levels"];

  _libMain      = 0;
  _mainMenu     = 0;
  _main         = 0;
  _window       = new sf::RenderWindow();
  _pause        = false;
  _fullscreen   = false;
  _syncEndLevel = false;
  _windowTitle  = "Shinygami Powered Game";
  if (levels)
  {
    Data::iterator it  = levels.begin();
    Data::iterator end = levels.end();

    for (; it != end ; ++it)
    {
      std::pair<std::string, std::string> level;

      level.first  = (*it).Key();
      level.second = (*it).Value();
      std::cout << "Niveau: " << level.first << std::endl;
      _levelList.push_back(level);
    }
  }

  ScriptFactory scriptFactory = 0;
  DataTree*     mainMenu      = DataTree::Factory::Yaml("mainmenu.yml");

  if (mainMenu)
  {
    _running      = false;
    _mainMenu     = new Widgets::Ui(mainMenu);
    _libMain      = LoadedLib::Factory::New(std::string("./main") + LIB_EXTENSION);
    if (_libMain)
    {
      scriptFactory = _libMain->GetFunction<ScriptFactory>("NewMainScript");
      if (scriptFactory)
        _main         = scriptFactory(*this, *_mainMenu);
      else
        std::cerr << "Game: Failed to load MainScript: " << _libMain->GetLastError() << std::endl;
    }
    delete mainMenu;
  }
}

Game::~Game()
{
  if (_main)       delete _main;
  if (_libMain)    delete _libMain;
  if (_mainMenu)   delete _mainMenu;
  if (_window)     delete _window;
  RessourceData<sf::Texture>::Get()->Clean();
  RessourceData<sf::Sound>::Get()->Clean();
}

void Game::LoadLevel(LevelFactory factory, const std::string& name)
{
  if (!(factory))
    ErrorManager::Get()->AddError("Game::LoadLevel: pointer to LevelFactory is null.", ErrorManager::Warning);
  else
  {
    LevelList::iterator it    = _levelList.begin();
    LevelList::iterator end   = _levelList.end();
    Level*              level = 0;

    for (; it != end ; ++it)
    {
      if ((*it).first == name)
      {
        DataTree* data  = DataTree::Factory::Yaml((*it).second);

        if (data)
          level = factory("./" + (*it).first + "/", data);
        else
          ErrorManager::Get()->AddError("Unable to load file " + (*it).second, ErrorManager::Warning);
        break ;
      }
    }
    if (level)
    {
      _currentLevels.push(level);
    }
    else
      ErrorManager::Get()->AddError("Game can't load level (no such level, or the Factory crashed)", ErrorManager::Warning);
  }
}

Level* Game::CurrentLevel(void)
{
  if (_currentLevels.empty())
    return (0);
  return (_currentLevels.top());
}

void Game::EndLevel(void)
{
  _syncEndLevel = true;
}

void Game::DoEndLevel(void)
{
  if (!(_currentLevels.empty()))
  {
    Level* level = _currentLevels.top();

    delete level;
    _currentLevels.pop();
    _syncEndLevel = false;
  }
}

void Game::ExecuteLevel(void)
{
  Level*             level     = _currentLevels.top();

  level->Hud()->SetVisible(true);
  _mainMenu->SetVisible(false); // This will ensure MainMenu doesn't receive any events

  level->Update();
  level->Render(*_window);
  if (_syncEndLevel)
    DoEndLevel();
}

void Game::ExecuteMainMenu(void)
{
  _main->DrawMenuBackground(*_window);
  if (!(_mainMenu->IsVisible()))
    _mainMenu->SetVisible(true);
  _mainMenu->Render(*_window);
}

int Game::Execute(void)
{
  if (_running)
    return (-1);
  if (!_main)
  {
    std::cerr << "MainScript isn't loaded" << std::endl;
    return (-2);
  }
  _running = true;
  do
  {
    if (!_pause && _currentLevels.size())
      ExecuteLevel();
    else
      ExecuteMainMenu();
    _window->Display();
    _soundManager.Run();
  } while (_running && _errorManager.Run() && _eventManager.Run(*_window));
  _running = false;
  return (0);
}

void Game::SetResolution(const sf::Vector2f& size)
{
  sf::VideoMode videoMode(_window->GetWidth(), _window->GetHeight());

  if (!_window || (videoMode.Height == size.y && videoMode.Width == size.x))
    return ;
  _window->Create(sf::VideoMode(size.x, size.y), _windowTitle, (_fullscreen ? sf::Style::Fullscreen : 0));
}

void Game::SetFullscreen(bool set)
{
  if (set == _fullscreen || !_window)
    return ;
  sf::VideoMode videoMode(_window->GetWidth(), _window->GetHeight());

  _fullscreen = set;
  _window->Create(videoMode, _windowTitle, (_fullscreen ? sf::Style::Fullscreen : 0));
}

void Game::SetWindowTitle(const std::string& name)
{
  _windowTitle = name;
  if (_window)
    _window->SetTitle(name);
}

void Game::SetMainScript(MainScript* script)
{
  _main = script;
}

sf::Vector2f Game::ScreenSize(void) const
{
  return (sf::Vector2f(_window->GetWidth(), _window->GetHeight()));
}
